//
// Copyright (c) 2004 K. Wilkins
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from
// the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
//    be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//

//////////////////////////////////////////////////////////////////////////////
//                       Handy - An Atari Lynx Emulator                     //
//                          Copyright (c) 1996,1997                         //
//                                 K. Wilkins                               //
//////////////////////////////////////////////////////////////////////////////
// System object header file                                                //
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// This header file provides the interface definition and inline code for   //
// the system object, this object if what binds together all of the Handy   //
// hardware enmulation objects, its the glue that holds the system together //
//                                                                          //
//    K. Wilkins                                                            //
// August 1997                                                              //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////
// Revision History:                                                        //
// -----------------                                                        //
//                                                                          //
// 01Aug1997 KW Document header added & class documented.                   //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

#ifndef SYSTEM_H
#define SYSTEM_H

#include "machine.h"

#include <string>
#include <algorithm>

#define HANDY_SYSTEM_FREQ						16000000
#define HANDY_TIMER_FREQ						20

#define HANDY_FILETYPE_LNX		0
#define HANDY_FILETYPE_HOMEBREW	1
#define HANDY_FILETYPE_SNAPSHOT	2
#define HANDY_FILETYPE_ILLEGAL	3

#define HANDY_SCREEN_WIDTH	160
#define HANDY_SCREEN_HEIGHT	102
//
// Define the global variable list
//

/*
#ifdef SYSTEM_CPP
uint32   gSuzieDoneTime = 0;
uint32	gSystemCycleCount=0;
uint32	gNextTimerEvent=0;
uint32	gCPUBootAddress=0;
uint32	gSystemIRQ=FALSE;
uint32	gSystemNMI=FALSE;
uint32	gSystemCPUSleep=FALSE;
uint32	gSystemHalt=FALSE;
#else
extern uint32	gSystemCycleCount;
extern uint32	gSuzieDoneTime;
extern uint32	gNextTimerEvent;
extern uint32	gCPUBootAddress;
extern uint32	gSystemIRQ;
extern uint32	gSystemNMI;
extern uint32	gSystemCPUSleep;
extern uint32	gSystemHalt;
#endif
*/

//
// Define the interfaces before we start pulling in the classes
// as many classes look for articles from the interfaces to
// allow compilation

#include "sysbase.h"

class CSystem;

//
// Now pull in the parts that build the system
//
#include "lynxbase.h"
#include "ram.h"
#include "rom.h"
#include "memmap.h"
#include "cart.h"
#include "susie.h"
#include "mikie.h"
#include "c65c02.h"

#define TOP_START	0xfc00
#define TOP_MASK	0x03ff
#define TOP_SIZE	0x400
#define SYSTEM_SIZE	65536

class CSystem : public CSystemBase
{
public:
	CSystem(const uint8 *, uint32, const uint8*, uint32, int, int, bool) MDFN_COLD;
	~CSystem() MDFN_COLD;

public:
	void Reset() MDFN_COLD;

	inline void Update(uint32 targetclock)
	{
		// Only update if there is a predicted timer event
		if(gSystemCycleCount >= gNextTimerEvent)
		{
			mMikie->Update();
		}

		// Step the processor through 1 instruction
		mCpu->Update();

		// If the CPU is asleep then skip to the next timer event
		if(gSystemCPUSleep)
		{
			gSystemCycleCount = std::min(gNextTimerEvent, targetclock);
		}
	}

	void Blit(const uint32 *src);

	bool Advance(int buttons, uint32 *vbuff, int16 *sbuff, int &sbuffsize);
	bool GetSaveRamPtr(int &size, uint8 *&data) { return mCart->GetSaveRamPtr(size, data); }
	void GetReadOnlyCartPtrs(int &s0, uint8 *&p0, int &s1, uint8 *&p1) { mCart->GetReadOnlyPtrs(s0, p0, s1, p1); }

	//
	// We MUST have separate CPU & RAM peek & poke handlers as all CPU accesses must
	// go thru the address generator at $FFF9
	//
	// BUT, Mikie video refresh & Susie see the whole system as RAM
	//
	// Complete and utter wankers, its taken me 1 week to find the 2 lines
	// in all the documentation that mention this fact, the mother of all
	// bugs has been found and FIXED.......

	// CPU
	inline void  Poke_CPU(uint32 addr, uint8 data) { mMemoryHandlers[addr]->Poke(addr,data);};
	inline uint8 Peek_CPU(uint32 addr) { return mMemoryHandlers[addr]->Peek(addr);};
	inline void  PokeW_CPU(uint32 addr,uint16 data) { mMemoryHandlers[addr]->Poke(addr,data&0xff);addr++;mMemoryHandlers[addr]->Poke(addr,data>>8);};
	inline uint16 PeekW_CPU(uint32 addr) {return ((mMemoryHandlers[addr]->Peek(addr))+(mMemoryHandlers[addr]->Peek(addr+1)<<8));};

	// RAM
	inline void  Poke_RAM(uint32 addr, uint8 data) { mRam->Poke(addr,data);};
	inline uint8 Peek_RAM(uint32 addr) { return mRam->Peek(addr);};
	inline void  PokeW_RAM(uint32 addr,uint16 data) { mRam->Poke(addr,data&0xff);addr++;mRam->Poke(addr,data>>8);};
	inline uint16 PeekW_RAM(uint32 addr) {return ((mRam->Peek(addr))+(mRam->Peek(addr+1)<<8));};

	// High level cart access for debug etc
	inline void  Poke_CART(uint32 addr, uint8 data) {mCart->Poke(addr,data);};
	inline uint8 Peek_CART(uint32 addr) {return mCart->Peek(addr);};
	inline void  CartBank(EMMODE bank) {mCart->BankSelect(bank);};
	inline uint32 CartSize() {return mCart->ObjectSize();};

	// Low level cart access for Suzy, Mikey
	inline void  Poke_CARTB0(uint8 data) {mCart->Poke0(data);};
	inline void  Poke_CARTB1(uint8 data) {mCart->Poke1(data);};
	inline uint8 Peek_CARTB0() {return mCart->Peek0();}
	inline uint8 Peek_CARTB1() {return mCart->Peek1();}
	inline void  CartAddressStrobe(bool strobe) {mCart->CartAddressStrobe(strobe);};
	inline void  CartAddressData(bool data) {mCart->CartAddressData(data);};

	// Low level CPU access
	void	SetRegs(C6502_REGS &regs) {mCpu->SetRegs(regs);};
	void	GetRegs(C6502_REGS &regs) {mCpu->GetRegs(regs);};

	// Mikey system interfacing
	void	ComLynxCable(int status) { mMikie->ComLynxCable(status); };
	void	ComLynxRxData(int data)  { mMikie->ComLynxRxData(data); };
	void	ComLynxTxCallback(void (*function)(int data,uint32 objref),uint32 objref) { mMikie->ComLynxTxCallback(function,objref); };

	// Suzy system interfacing
	uint32	PaintSprites() {return mSusie->PaintSprites();};

	// Miscellaneous
	void	SetButtonData(uint32 data);
	// uint32	GetButtonData() {return mSusie->GetButtonData();};
	uint8*	GetRamPointer() {return mRam->GetRamPointer();};

public:
	CLynxBase		*mMemoryHandlers[SYSTEM_SIZE];
	CCart			*mCart;
	CRom			*mRom;
	CMemMap			*mMemMap;
	CRam			*mRam;
	C65C02			*mCpu;
	CMikie			*mMikie;
	CSusie			*mSusie;

	// old globals
	uint32			gSuzieDoneTime;
	uint32			gSystemCycleCount;
	uint32			gNextTimerEvent;
	uint32			gSystemIRQ;
	uint32			gSystemNMI;
	uint32			gSystemCPUSleep;
	uint32			gSystemHalt; // this is set in various places, but never tested, anywhere?

	// frame overflow detection
	int frameoverflow;
	// rotation of the device
	int rotate;
	// video dest
	uint32 *videobuffer;

	template<bool isReader>void SyncState(NewState *ns);
};

#endif
